"""
Unified Configuration Management System
Centralizes all configuration for the ArXiv subscription platform
"""

import os
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, field
from dotenv import load_dotenv
import yaml
import json

# Load environment variables
load_dotenv()

@dataclass
class DatabaseConfig:
    """Database configuration"""
    url: str = field(default_factory=lambda: os.getenv("DATABASE_URL", "postgresql://arxiv_user:password@localhost:5432/arxiv_platform"))
    pool_size: int = field(default_factory=lambda: int(os.getenv("DB_POOL_SIZE", "10")))
    max_overflow: int = field(default_factory=lambda: int(os.getenv("DB_MAX_OVERFLOW", "20")))
    echo: bool = field(default_factory=lambda: os.getenv("DB_ECHO", "false").lower() == "true")

@dataclass
class RedisConfig:
    """Redis configuration"""
    url: str = field(default_factory=lambda: os.getenv("REDIS_URL", "redis://localhost:6379/0"))
    max_connections: int = field(default_factory=lambda: int(os.getenv("REDIS_MAX_CONNECTIONS", "20")))
    timeout: int = field(default_factory=lambda: int(os.getenv("REDIS_TIMEOUT", "5")))

@dataclass
class SupabaseConfig:
    """Supabase authentication configuration"""
    url: str = field(default_factory=lambda: os.getenv("SUPABASE_URL", ""))
    anon_key: str = field(default_factory=lambda: os.getenv("SUPABASE_ANON_KEY", ""))
    service_role_key: str = field(default_factory=lambda: os.getenv("SUPABASE_SERVICE_ROLE_KEY", ""))

@dataclass
class APIConfig:
    """API server configuration"""
    host: str = field(default_factory=lambda: os.getenv("HOST", "0.0.0.0"))
    port: int = field(default_factory=lambda: int(os.getenv("PORT", "8000")))
    cors_origins: List[str] = field(default_factory=lambda: os.getenv("CORS_ORIGINS", "http://localhost:3000").split(","))
    allowed_hosts: List[str] = field(default_factory=lambda: os.getenv("ALLOWED_HOSTS", "localhost,127.0.0.1").split(","))
    jwt_secret: str = field(default_factory=lambda: os.getenv("JWT_SECRET", "change-in-production"))
    rate_limit_per_minute: int = field(default_factory=lambda: int(os.getenv("RATE_LIMIT_PER_MINUTE", "60")))

@dataclass
class EmailConfig:
    """Email service configuration"""
    smtp_host: str = field(default_factory=lambda: os.getenv("SMTP_HOST", "smtp.gmail.com"))
    smtp_port: int = field(default_factory=lambda: int(os.getenv("SMTP_PORT", "587")))
    smtp_user: str = field(default_factory=lambda: os.getenv("SMTP_USER", ""))
    smtp_password: str = field(default_factory=lambda: os.getenv("SMTP_PASSWORD", ""))
    from_email: str = field(default_factory=lambda: os.getenv("FROM_EMAIL", "noreply@arxivsubscribe.com"))
    from_name: str = field(default_factory=lambda: os.getenv("FROM_NAME", "ArXiv Subscription Platform"))
    use_tls: bool = field(default_factory=lambda: os.getenv("EMAIL_USE_TLS", "true").lower() == "true")

@dataclass
class ScraperConfig:
    """ArXiv scraper configuration"""
    oai_base_url: str = field(default_factory=lambda: os.getenv("ARXIV_OAI_URL", "http://export.arxiv.org/oai2"))
    rate_limit_delay: float = field(default_factory=lambda: float(os.getenv("SCRAPING_RATE_LIMIT", "3.0")))
    max_retries: int = field(default_factory=lambda: int(os.getenv("SCRAPING_MAX_RETRIES", "5")))
    batch_size: int = field(default_factory=lambda: int(os.getenv("SCRAPING_BATCH_SIZE", "100")))
    subjects: List[str] = field(default_factory=lambda: ["math-ph", "math.AP", "math.NA", "cs.NA"])
    earliest_date: str = field(default_factory=lambda: os.getenv("SCRAPING_EARLIEST_DATE", "2007-01-01"))
    scheduling_enabled: bool = field(default_factory=lambda: os.getenv("SCHEDULING_ENABLED", "true").lower() == "true")
    daily_run_time: str = field(default_factory=lambda: os.getenv("SCRAPER_DAILY_RUN_TIME", "02:00"))

@dataclass
class AIConfig:
    """AI classification engine configuration"""
    model_name: str = field(default_factory=lambda: os.getenv("AI_MODEL_NAME", "all-MiniLM-L6-v2"))
    use_gpu: bool = field(default_factory=lambda: os.getenv("AI_USE_GPU", "false").lower() == "true")
    batch_size: int = field(default_factory=lambda: int(os.getenv("AI_BATCH_SIZE", "32")))
    cache_embeddings: bool = field(default_factory=lambda: os.getenv("AI_CACHE_EMBEDDINGS", "true").lower() == "true")
    embedding_cache_size: int = field(default_factory=lambda: int(os.getenv("AI_CACHE_SIZE", "10000")))
    
    # Learning parameters
    time_decay_factor: float = field(default_factory=lambda: float(os.getenv("AI_TIME_DECAY", "0.95")))
    min_interactions: int = field(default_factory=lambda: int(os.getenv("AI_MIN_INTERACTIONS", "5")))
    
    # Interaction weights for recommendation learning
    interaction_weights: Dict[str, float] = field(default_factory=lambda: {
        "view": 1.0,
        "download": 3.0,
        "bookmark": 5.0,
        "rate": 4.0,
        "like": 2.0
    })

@dataclass
class MonitoringConfig:
    """Monitoring and metrics configuration"""
    prometheus_port: int = field(default_factory=lambda: int(os.getenv("PROMETHEUS_PORT", "9090")))
    grafana_password: str = field(default_factory=lambda: os.getenv("GRAFANA_PASSWORD", "admin"))
    enabled: bool = field(default_factory=lambda: os.getenv("MONITORING_ENABLED", "true").lower() == "true")
    metrics_path: str = field(default_factory=lambda: os.getenv("METRICS_PATH", "/metrics"))

@dataclass
class SecurityConfig:
    """Security configuration"""
    session_timeout_minutes: int = field(default_factory=lambda: int(os.getenv("SESSION_TIMEOUT_MINUTES", "1440")))
    password_min_length: int = field(default_factory=lambda: int(os.getenv("PASSWORD_MIN_LENGTH", "8")))
    password_require_symbols: bool = field(default_factory=lambda: os.getenv("PASSWORD_REQUIRE_SYMBOLS", "true").lower() == "true")
    enable_2fa: bool = field(default_factory=lambda: os.getenv("ENABLE_2FA", "false").lower() == "true")
    max_login_attempts: int = field(default_factory=lambda: int(os.getenv("MAX_LOGIN_ATTEMPTS", "5")))
    lockout_duration_minutes: int = field(default_factory=lambda: int(os.getenv("LOCKOUT_DURATION_MINUTES", "15")))

@dataclass
class FeatureFlags:
    """Feature flags configuration"""
    enable_recommendations: bool = field(default_factory=lambda: os.getenv("ENABLE_RECOMMENDATIONS", "true").lower() == "true")
    enable_email_notifications: bool = field(default_factory=lambda: os.getenv("ENABLE_EMAIL_NOTIFICATIONS", "true").lower() == "true")
    enable_analytics: bool = field(default_factory=lambda: os.getenv("ENABLE_ANALYTICS", "true").lower() == "true")
    enable_api_access: bool = field(default_factory=lambda: os.getenv("ENABLE_API_ACCESS", "true").lower() == "true")
    enable_social_auth: bool = field(default_factory=lambda: os.getenv("ENABLE_SOCIAL_AUTH", "false").lower() == "true")

@dataclass
class SystemConfig:
    """System-wide configuration"""
    environment: str = field(default_factory=lambda: os.getenv("ENVIRONMENT", "development"))
    debug: bool = field(default_factory=lambda: os.getenv("DEBUG", "false").lower() == "true")
    log_level: str = field(default_factory=lambda: os.getenv("LOG_LEVEL", "INFO"))
    storage_path: str = field(default_factory=lambda: os.getenv("STORAGE_PATH", "./data"))
    log_path: str = field(default_factory=lambda: os.getenv("LOG_PATH", "./logs"))
    
    # Backup configuration
    backup_enabled: bool = field(default_factory=lambda: os.getenv("BACKUP_ENABLED", "true").lower() == "true")
    backup_retention_days: int = field(default_factory=lambda: int(os.getenv("BACKUP_RETENTION_DAYS", "30")))

@dataclass
class AppConfig:
    """Complete application configuration"""
    database: DatabaseConfig = field(default_factory=DatabaseConfig)
    redis: RedisConfig = field(default_factory=RedisConfig)
    supabase: SupabaseConfig = field(default_factory=SupabaseConfig)
    api: APIConfig = field(default_factory=APIConfig)
    email: EmailConfig = field(default_factory=EmailConfig)
    scraper: ScraperConfig = field(default_factory=ScraperConfig)
    ai: AIConfig = field(default_factory=AIConfig)
    monitoring: MonitoringConfig = field(default_factory=MonitoringConfig)
    security: SecurityConfig = field(default_factory=SecurityConfig)
    features: FeatureFlags = field(default_factory=FeatureFlags)
    system: SystemConfig = field(default_factory=SystemConfig)

    def to_dict(self) -> Dict[str, Any]:
        """Convert configuration to dictionary"""
        return {
            "database": {
                "url": self.database.url,
                "pool_size": self.database.pool_size,
                "max_overflow": self.database.max_overflow,
                "echo": self.database.echo
            },
            "redis": {
                "url": self.redis.url,
                "max_connections": self.redis.max_connections,
                "timeout": self.redis.timeout
            },
            "supabase": {
                "url": self.supabase.url,
                "anon_key": self.supabase.anon_key
                # Don't include service role key in serialization
            },
            "api": {
                "host": self.api.host,
                "port": self.api.port,
                "cors_origins": self.api.cors_origins,
                "allowed_hosts": self.api.allowed_hosts,
                "rate_limit_per_minute": self.api.rate_limit_per_minute
            },
            "email": {
                "smtp_host": self.email.smtp_host,
                "smtp_port": self.email.smtp_port,
                "from_email": self.email.from_email,
                "from_name": self.email.from_name,
                "use_tls": self.email.use_tls
            },
            "scraper": {
                "oai_base_url": self.scraper.oai_base_url,
                "rate_limit_delay": self.scraper.rate_limit_delay,
                "max_retries": self.scraper.max_retries,
                "batch_size": self.scraper.batch_size,
                "subjects": self.scraper.subjects,
                "earliest_date": self.scraper.earliest_date,
                "scheduling_enabled": self.scraper.scheduling_enabled,
                "daily_run_time": self.scraper.daily_run_time
            },
            "ai": {
                "model_name": self.ai.model_name,
                "use_gpu": self.ai.use_gpu,
                "batch_size": self.ai.batch_size,
                "cache_embeddings": self.ai.cache_embeddings,
                "embedding_cache_size": self.ai.embedding_cache_size,
                "time_decay_factor": self.ai.time_decay_factor,
                "min_interactions": self.ai.min_interactions,
                "interaction_weights": self.ai.interaction_weights
            },
            "monitoring": {
                "prometheus_port": self.monitoring.prometheus_port,
                "enabled": self.monitoring.enabled,
                "metrics_path": self.monitoring.metrics_path
            },
            "security": {
                "session_timeout_minutes": self.security.session_timeout_minutes,
                "password_min_length": self.security.password_min_length,
                "password_require_symbols": self.security.password_require_symbols,
                "enable_2fa": self.security.enable_2fa,
                "max_login_attempts": self.security.max_login_attempts,
                "lockout_duration_minutes": self.security.lockout_duration_minutes
            },
            "features": {
                "enable_recommendations": self.features.enable_recommendations,
                "enable_email_notifications": self.features.enable_email_notifications,
                "enable_analytics": self.features.enable_analytics,
                "enable_api_access": self.features.enable_api_access,
                "enable_social_auth": self.features.enable_social_auth
            },
            "system": {
                "environment": self.system.environment,
                "debug": self.system.debug,
                "log_level": self.system.log_level,
                "storage_path": self.system.storage_path,
                "log_path": self.system.log_path,
                "backup_enabled": self.system.backup_enabled,
                "backup_retention_days": self.system.backup_retention_days
            }
        }

    @classmethod
    def from_file(cls, config_path: str) -> 'AppConfig':
        """Load configuration from YAML file"""
        try:
            with open(config_path, 'r') as f:
                if config_path.endswith('.yaml') or config_path.endswith('.yml'):
                    data = yaml.safe_load(f)
                elif config_path.endswith('.json'):
                    data = json.load(f)
                else:
                    raise ValueError(f"Unsupported config file format: {config_path}")
            
            # Override with loaded data
            config = cls()
            # Update config with loaded data (implementation depends on structure)
            return config
            
        except FileNotFoundError:
            # Return default config if file not found
            return cls()
        except Exception as e:
            print(f"Warning: Could not load config file {config_path}: {e}")
            return cls()

    def save_to_file(self, config_path: str):
        """Save configuration to file"""
        config_dict = self.to_dict()
        
        with open(config_path, 'w') as f:
            if config_path.endswith('.yaml') or config_path.endswith('.yml'):
                yaml.dump(config_dict, f, default_flow_style=False)
            elif config_path.endswith('.json'):
                json.dump(config_dict, f, indent=2)
            else:
                raise ValueError(f"Unsupported config file format: {config_path}")

# Global configuration instance
app_config = AppConfig()

# Convenience functions
def get_config() -> AppConfig:
    """Get the global configuration instance"""
    return app_config

def load_config(config_path: Optional[str] = None) -> AppConfig:
    """Load configuration from file or environment"""
    global app_config
    
    if config_path:
        app_config = AppConfig.from_file(config_path)
    else:
        # Try to load from default locations
        for path in ['config.yaml', 'config.yml', 'config.json', '.env']:
            if os.path.exists(path):
                app_config = AppConfig.from_file(path)
                break
    
    return app_config

def validate_config(config: AppConfig) -> List[str]:
    """Validate configuration and return list of issues"""
    issues = []
    
    # Database validation
    if not config.database.url:
        issues.append("Database URL is required")
    
    # Email validation (if email features enabled)
    if config.features.enable_email_notifications:
        if not config.email.smtp_host:
            issues.append("SMTP host is required when email notifications are enabled")
        if not config.email.smtp_user:
            issues.append("SMTP user is required when email notifications are enabled")
    
    # Supabase validation (if using Supabase auth)
    if config.supabase.url and not config.supabase.anon_key:
        issues.append("Supabase anon key is required when Supabase URL is provided")
    
    # Security validation
    if config.system.environment == "production":
        if config.api.jwt_secret == "change-in-production":
            issues.append("JWT secret must be changed in production")
        if config.system.debug:
            issues.append("Debug mode should be disabled in production")
    
    return issues

# Export commonly used configurations
database_config = app_config.database
redis_config = app_config.redis
api_config = app_config.api
email_config = app_config.email
scraper_config = app_config.scraper
ai_config = app_config.ai